package cn.stylefeng.guns.kss.controller;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.stylefeng.guns.base.auth.context.LoginContextHolder;
import cn.stylefeng.guns.base.auth.model.LoginUser;
import cn.stylefeng.guns.base.log.BussinessLog;
import cn.stylefeng.guns.kss.Key;
import cn.stylefeng.guns.kss.KeyPoolManager;
import cn.stylefeng.guns.kss.PolicyEngine;
import cn.stylefeng.guns.sys.core.constant.dictmap.RoleDict;
import cn.stylefeng.guns.sys.core.util.TestDES;
import cn.stylefeng.guns.sys.modular.system.entity.*;
import cn.stylefeng.guns.sys.modular.system.mapper.OperationLogMapper;
import cn.stylefeng.guns.sys.modular.system.service.KmsAppService;
import cn.stylefeng.guns.sys.modular.system.service.KmsPoolService;
import cn.stylefeng.guns.sys.modular.system.service.KmsService;
import cn.stylefeng.guns.sys.modular.system.service.SysQnlKeyService;
import cn.stylefeng.roses.core.base.controller.BaseController;
import cn.stylefeng.roses.kernel.model.response.ResponseData;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * API to create a new key and get an already existing key.
 */

@RestController
public class KMSApiController extends BaseController {

    @Autowired
    private KeyPoolManager keyPoolMgr;
    @Autowired
    private PolicyEngine policy;
    private static final Logger logger = LoggerFactory.getLogger(KMSApiController.class);
    @Autowired
    private SysQnlKeyService sysQnlKeyService;
    @Autowired
    KmsService kmsService;
    @Autowired
    OperationLogMapper operationLogMapper;
    @Autowired
    KmsAppService kmsAppService;
    @Autowired
    private KmsPoolService kmsPoolService;

    /**
     * Request a new key from KMS. KMS generating the key is considered
     * a source site while the site requesting the key is considered a
     * destination site. The determination of which key pool to use is
     * made by using source and destination site id. Local site id of the KMS
     * is always the source site for this call.
     *
     * @param name Id of the KMS site where the new key request is coming from.
     * @return Key  New key
     */
    @RequestMapping("/newkey")
    public ResponseData newKey(@RequestParam(value = "token") String token,@RequestParam(value = "siteid") String name,
                               @RequestParam(value = "appName") String  appName,@RequestParam(value = "") String fileName) {
        Key k;

        QueryWrapper<KmsApp> srcwrapper = new QueryWrapper<>();
        QueryWrapper<KmsApp> destwrapper = new QueryWrapper<>();
        srcwrapper.eq("token", token);
        destwrapper.eq("name", appName);
        KmsApp srcApp = kmsAppService.getOne(srcwrapper);
        KmsApp destApp = kmsAppService.getOne(destwrapper);
        if(srcApp.getId().equals(destApp.getId())){
            return ResponseData.error("无法给自己发送文件");
        }
        long startTime = System.currentTimeMillis();
        if (policy.check()) {
            k = keyPoolMgr.newKey(name);
            printKey(k, true);
        } else {
            k = null;
        }
        long endTime = System.currentTimeMillis();
        long l = endTime - startTime;
        logger.info("生成密钥耗时：" + l + "毫秒");

        Kms kms = new Kms();
        if (k != null && k.index != -2) {
            kms.setBlockId(k.blockId);
            kms.setKey(k.hexKey);
            kms.setIndex((int) k.index);
            kms.setDestId(name);
            kms.setFileName("encryptFiler"+fileName);
            kms.setSrcAppId(srcApp.getId());
            kms.setDestAppId(destApp.getId());
            kmsService.save(kms);
            //修改
            QueryWrapper<SysQnlKey> wrapper = new QueryWrapper<>();
            wrapper.eq("key_", kms.getKey());
            wrapper.eq("dest_site", kms.getDestId()+"_0");
            SysQnlKey sysQnlKey = sysQnlKeyService.getOne(wrapper);
            sysQnlKey.setStatus("已使用");
            sysQnlKeyService.saveOrUpdate(sysQnlKey);
            try {
                OperationLog operationLog = new OperationLog();
                operationLog.setLogType("操作日志");
                operationLog.setLogName("生成密钥");
                operationLog.setUserId(1L);
                operationLog.setClassName(this.getClass().getName());
                operationLog.setMethod("newKey");
                operationLog.setCreateTime(new Date());
                operationLog.setSucceed("成功");
                operationLog.setMessage("生成"+srcApp.getName()+"---->"+destApp.getName()+"密钥成功! 耗时"+">>生成密钥耗时：" + l + "毫秒");
                operationLogMapper.insert(operationLog);
            } catch (Exception e) {
                logger.error("创建业务日志异常!", e);
            }
            try {
                QueryWrapper<KmsPool> kmsPoolQueryWrapper = new QueryWrapper<>();
                kmsPoolQueryWrapper.eq("back_id",kms.getBlockId());
                KmsPool one = kmsPoolService.getOne(kmsPoolQueryWrapper);
                if(one == null) {
                    Properties properties = new Properties();
                    // 使用ClassLoader加载properties配置文件生成对应的输入流
                    InputStream in = new FileInputStream(new File(System.getProperty("user.home")+"/.qkd/kms/site.properties"));
                    // 使用properties对象加载输入流
                    properties.load(in);
                    //获取key对应的value值
                    String property = properties.getProperty("kms.site.id");
                    String srcPath =System.getProperty("user.home")+"/.qkd/kms/pools/"+property+"/"+name+"/"+kms.getBlockId();
                    List<String> strings = FileUtil.readLines(srcPath, "UTF-8");
                    String join = CollectionUtil.join(strings, ",");
                    KmsPool kmsPool = new KmsPool();
                    kmsPool.setBackId(kms.getBlockId());
                    kmsPool.setDestSite(name);
                    kmsPool.setSrcSite(property);
                    kmsPool.setFilePath(srcPath);
                    kmsPool.setKey(join);
                    kmsPoolService.save(kmsPool);
                }
            }catch (Exception e) {
                e.printStackTrace();
            }

            return ResponseData.success(kms);
        }
        else {
            return ResponseData.error("密钥生成失败");
        }
    }


    /**
     *
     * @param id
     * @return
     */
    @RequestMapping("/delete")
    @BussinessLog(value = "删除秘钥", key = "name", dict = RoleDict.class)
    public ResponseData newKey(@RequestParam(value = "id") Long id) {
        kmsService.removeById(id);
        LoginUser user = LoginContextHolder.getContext().getUser();
        try {
            OperationLog operationLog = new OperationLog();
            operationLog.setLogType("操作日志");
            operationLog.setLogName("删除密钥");
            operationLog.setUserId(1L);
            operationLog.setClassName(this.getClass().getName());
            operationLog.setMethod("newKey");
            operationLog.setCreateTime(new Date());
            operationLog.setSucceed("成功");
            operationLog.setMessage("用户"+user.getAccount()+"删除");
            operationLogMapper.insert(operationLog);
        } catch (Exception e) {
            logger.error("创建业务日志异常!", e);
        }
        return ResponseData.success("删除成功");
    }

    /**
     *
     * @param id
     * @return
     */
    @RequestMapping(value = "/getKeyApp",method = RequestMethod.GET)
    public ResponseData getKeyApp(@RequestParam(value = "id") Long id,@RequestParam("token") String token) {
        Kms key = kmsService.getById(id);
        QueryWrapper<KmsApp> srcwrapper = new QueryWrapper<>();
        srcwrapper.eq("token", token);
        KmsApp destApp = kmsAppService.getOne(srcwrapper);
        try {
            OperationLog operationLog = new OperationLog();
            operationLog.setLogType("操作日志");
            operationLog.setLogName("获取密钥");
            operationLog.setUserId(1L);
            operationLog.setClassName(this.getClass().getName());
            operationLog.setMethod("newKey");
            operationLog.setCreateTime(new Date());
            operationLog.setSucceed("成功");
            operationLog.setMessage(""+destApp.getName()+"获取密钥成功!"+">>");
            operationLogMapper.insert(operationLog);
        } catch (Exception e) {
            logger.error("创建业务日志异常!", e);
        }
        if(key.getDestAppId().equals(destApp.getId())) {
            return ResponseData.success(key.getKey());
        }
        else {
            return ResponseData.error("非法请求");
        }
    }

    /**
     * layui上传组件 通用文件上传接口
     *
     * @author fengshuonan
     * @Date 2019-2-23 10:48:29
     */
    @RequestMapping(method = RequestMethod.POST, path = "/kms/upload")
    @ResponseBody
    public ResponseData layuiUpload(@RequestPart("file") MultipartFile file, String idList) {
        //生成秘钥
        Key k;
        LoginUser user = LoginContextHolder.getContext().getUser();
        long startTime = System.currentTimeMillis();
        if (policy.check()) {
            k = keyPoolMgr.newKey("B");
            printKey(k, true);
        } else {
            k = new Key();
        }
        long endTime = System.currentTimeMillis();
        long l = endTime - startTime;
        logger.info("生成密钥耗时：" + l + "毫秒");
        if (k != null && k.index != -2) {
            //获取文件流
            try {
                InputStream inputStream = file.getInputStream();
                TestDES testDES = new TestDES(k.hexKey);
                //加密保存
                testDES.encrypt(inputStream, "D:\\aa.txt");
                String[] strings = idList.split(",");
                for (String strId : strings) {
                    Kms kms = new Kms();
                    kms.setBlockId(k.blockId);
                    kms.setKey(k.hexKey);
                    kms.setIndex((int) k.index);
                    kms.setDestId("B");
                    kms.setAppId(Long.valueOf(strId));
                    kms.setDestAppId(user.getId());
                    kms.setFileName(file.getOriginalFilename());
                    kmsService.save(kms);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }

//            Kms kms = new Kms();
//            kms.setBlockId(k.blockId);
//            kms.setKey(k.hexKey);
//            kms.setIndex((int) k.index);
//            kms.setDestId(name);
//            kmsService.save(kms);
        }
        //上传文件到ftp文件服务器
        //存储记录
        return ResponseData.success(0, "上传成功", null);
    }

    /**
     * Get a key from the supplied blockid at a given index.
     * The pool name/id used to get the key is constructed
     * from the local site id and supplied site id. For this
     * call local site id is considered the destination and supplied
     * site id is the source site.
     *
     * @param name  Source site id
     * @param block Block id of the key. It is created when a new
     *              key request is made.
     * @param index Index of the key within the block mentioned above.
     *              It is created when a new key request is made.
     * @return Key
     */
    @RequestMapping("/getkey")
    public Key getKey(@RequestParam(value = "siteid") String name, @RequestParam(value = "blockid") String block, @RequestParam(value = "index") long index) {
        Key k;
        long startTime = System.currentTimeMillis();
        if (policy.check()) {
            k = keyPoolMgr.getKey(name, block, index);
            printKey(k, false);
        } else {
            k = new Key();
        }
        long endTime = System.currentTimeMillis();
        long l = endTime - startTime;
        logger.info("获取密钥耗时：" + l + "毫秒");
        return k;
    }

    //https://stackoverflow.com/questions/44839753/returning-json-object-as-response-in-spring-boot
    @RequestMapping("/v1/keys/{slaveSAEID}/enc_keys")
    public String getEncKey(@PathVariable String slaveSAEID) {
        Key k;
        if (policy.check()) {
            k = keyPoolMgr.newKey(slaveSAEID);
            printKey(k, true);
        } else {
            k = new Key();
        }
        StringBuilder sb = new StringBuilder("{\"keys\":[");
        sb.append(k.toJsonString());
        sb.append("]}");
        return sb.toString();
    }

    @RequestMapping("/v1/keys/{slaveSAEID}/dec_keys")
    public String getDecKey(@PathVariable String slaveSAEID,
                            @RequestParam(value = "key_ID") String keyID) {
        Key k;
        if (policy.check()) {
            // KeyID: index-blockId
            int i = keyID.indexOf('-');
            String index = keyID.substring(0, i);
            String blockId = keyID.substring(i + 1);
            logger.info("slaveSAEID:" + slaveSAEID);
            logger.info("keyID:" + keyID);
            logger.info("index:" + index);
            logger.info("blockId:" + blockId);
            k = keyPoolMgr.getKey(slaveSAEID, blockId, Long.parseLong(index));
            printKey(k, false);
        } else {
            k = new Key();
        }
        StringBuilder sb = new StringBuilder("{\"keys\":[");
        sb.append(k.toJsonString());
        sb.append("]}");
        return sb.toString();
    }

    @GetMapping("/selectKmsListById")
    public ResponseData selectKmsListById(@RequestParam String token) {
        List<Map> kmsList = kmsService.selectKmsListById(token);
        return ResponseData.success(kmsList);
    }


    @RequestMapping("/v1/keys/{slaveSAEID}/status")
    public String getStatus(@PathVariable String slaveSAEID) {
        // key blocksize
        // key bytesize
        StringBuilder sb = new StringBuilder();
        sb.append("{");

        sb.append("\"source_KME_ID\": ");
        sb.append("\"");
        sb.append(keyPoolMgr.getLocalSiteId());
        sb.append("\"");
        sb.append(",");

        sb.append("\"target_KME_ID\": ");
        sb.append("\"");
        sb.append(slaveSAEID);
        sb.append("\"");
        sb.append(",");

        sb.append("\"master_KME_ID\": ");
        sb.append("\"");
        sb.append(keyPoolMgr.getLocalSiteId());
        sb.append("\"");
        sb.append(",");

        sb.append("\"slave_KME_ID\": ");
        sb.append("\"");
        sb.append(slaveSAEID);
        sb.append("\"");
        sb.append(",");

        sb.append("\"key_size\": ");
        sb.append(keyPoolMgr.getKeyByteSize());
        sb.append(",");

        sb.append("\"key_block_size\": ");
        sb.append(keyPoolMgr.getKeyBlockSize());

        sb.append("}");

        return sb.toString();
    }

    void printKey(Key k, boolean isNew) {
        String str = isNew ? "New Key:" : "Peer Key:";
        logger.info(str);
        logger.info(k.toString());
    }
}
